﻿using NVelocity;
using NVelocity.App;
using NVelocity.Runtime;
using System;
using System.Collections.Generic;
using System.Data;

namespace DevelopAssistant.AddIn.JavaProject
{
    public class JavaSpringBootHelper
    {
        private static readonly string templete="template";

        private static string currentDirectory
        {
            get
            {
               return AppDomain.CurrentDomain.BaseDirectory;
            }
        }

        private static bool ArrayContains(string[] array,string str)
        {
            bool result = false;
            if(array!=null && array.Length > 0)
            {
                for(int i = 0; i < array.Length; i++)
                {
                    if (string.Equals(array[i].ToString(), 
                        str, StringComparison.OrdinalIgnoreCase))
                    {
                        result = true;
                        break;
                    }
                }
            }
            return result;
        }

        private static string ToJavaDataType(string dataType)
        {
            string result = "String";
            switch (dataType.ToLower())
            {
                case "uuid":
                case "xml":
                case "text":
                case "varchar":
                case "nvarchar":
                case "character":
                case "character varying":
                    result = "String";
                    break;
                case "date":
                case "datetime":               
                case "timestamp":
                    result = "Date";
                    break;
                case "int":
                case "smallint":
                    result = "int";
                    break;
                case "integer":
                    result = "integer";
                    break;
                case "bigint":
                    result = "long";
                    break;
                case "numeric":
                    result = "Number";
                    break;
                case "float":
                    result = "float";
                    break;
                case "double":
                    result = "double";
                    break;
                case "bit":
                case "boolean":
                    result = "boolean";
                    break;
                case "bytea":                
                case "image":
                case "varbinary":
                    result = "byte[]";
                    break;
            }
            return result;
        }

        private static string ToIdType(string dataType)
        {
            string result = "UUID";
            return result;
        }

        private static string ToJdbcType(string dataType)
        {
            string result = "VARCHAR";

            switch (dataType.ToLower())
            {
                case "string":
                case "nvarchar":
                    result = "VARCHAR";
                    break;
                case "boolean":
                    result = "BOOLEAN";
                    break;
                case "byte":
                    result = "TINYINT";
                    break;
                case "byte[]":
                    result = "VARBINARY";
                    break;
                case "long":
                    result = "BIGINT";
                    break;
                case "decimal":
                case "numberic":
                    result = "NUMERIC";
                    break;
                case "float":
                    result = "REAL";
                    break;
                case "double":
                    result = "DOUBLE";
                    break;
                case "date":
                case "timestamp":
                    result = "TIMESTAMP";
                    break;
                case "time":
                    result = "TIME";
                    break;
                default:
                    result = "VARCHAR";
                    break;
            }

            return result;
        }

        private static string ToOrderBies(List<QueryEntity> orderbies)
        {
            string result = "";
            for (int i = 0; i < orderbies.Count; i++)
            {
                QueryEntity entity = orderbies[i];
                if (i == 0)
                {
                    result += entity.Column + " " + entity.Value;
                }
                else
                {
                    result += "," + entity.Column + " " + entity.Value;
                }
            }
            return result;
        }

        private static void DoColumnInfoParse(DataTableColumnInfo column,string cisnull)
        {
            string[] strings = cisnull.Split(',');
            if (ArrayContains(strings, "pk"))
            {
                column.IsParmaryKey = true;
            }
            if (ArrayContains(strings, "identity"))  
            {
                column.IsParmaryKey = true;
            }
            if (column.PropertyDataType.Equals("Date"))
            {
                column.JsonFormat = "yyyy-MM-dd hh:mm:ss";
            }
            if (string.IsNullOrEmpty(column.Description))
            {
                column.Description = column.ColumnName;
            }
        }

        private static ResultEntity GenerateEntityCode(string entityName, GenerateSettings settings, VelocityEngine vltEngine, VelocityContext vltContext)
        {
            ResultEntity result = new ResultEntity();            
            string codeText = "";

            try
            {
                Template vltTemplate = vltEngine.GetTemplate("EntityTemplate.tt");
                using (System.IO.StringWriter vltWriter = new System.IO.StringWriter())
                {
                    vltTemplate.Merge(vltContext, vltWriter);
                    codeText = vltWriter.GetStringBuilder().ToString();
                }
            }
            catch(Exception ex)
            {
                DevelopAssistant.Common.NLogger.LogError(ex);
            }
                      
            result.Name = entityName;
            result.TypeName = "Entity";
            result.SnippetCode = codeText;
            return result;
        }

        private static ResultEntity GenerateMapperCode(string entityName, GenerateSettings settings, VelocityEngine vltEngine, VelocityContext vltContext, List<DataTableColumnInfo> columns, QueryWrapper wrapper)
        {
            ResultEntity result = new ResultEntity();
            
            string codeText = "";

            try
            {
                Template vltTemplate = vltEngine.GetTemplate("MapperTemplate.tt");
                vltContext.Put("QueryWrapper", wrapper.ToList());
                vltContext.Put("OrderWrapper", ToOrderBies(wrapper.Orderbies()));

                DataTableColumnInfo parmaryKeyColumnInfo = null; // new DataTableColumnInfo();

                System.Text.StringBuilder columnsString = new System.Text.StringBuilder();
                System.Text.StringBuilder valuesString = new System.Text.StringBuilder();
                for (int i = 0; i < columns.Count; i++)
                {
                    DataTableColumnInfo column = columns[i];
                    if (i == 0)
                    {
                        columnsString.Append(column.ColumnName);
                        if (settings.AutoJdbcType)
                            valuesString.Append("#{" + column.PropertyName + ",jdbcType=" + column.JdbcType + "}");
                        else
                            valuesString.Append("#{" + column.PropertyName + "}");
                    }
                    else
                    {
                        columnsString.Append("," + column.ColumnName);
                        if (settings.AutoJdbcType)
                            valuesString.Append(",#{" + column.PropertyName + ",jdbcType=" + column.JdbcType + "}");
                        else
                            valuesString.Append(",#{" + column.PropertyName + "}");
                    }

                    if (column.IsParmaryKey)
                    {
                        parmaryKeyColumnInfo = (DataTableColumnInfo)column.Clone();
                    }

                }

                vltContext.Put("ColumnsString", columnsString.ToString());
                vltContext.Put("ValuesString", valuesString.ToString());
                vltContext.Put("ParmaryKeyColumnInfo", parmaryKeyColumnInfo);

                using (System.IO.StringWriter vltWriter = new System.IO.StringWriter())
                {
                    vltTemplate.Merge(vltContext, vltWriter);
                    codeText = vltWriter.GetStringBuilder().ToString();
                }
            }
            catch(Exception ex)
            {
                DevelopAssistant.Common.NLogger.LogError(ex);
            }

            result.Name = entityName;
            result.TypeName = "Mapper";
            result.SnippetCode = codeText;
            return result;
        }

        private static ResultEntity GenerateDaoCode(string entityName, GenerateSettings settings, VelocityEngine vltEngine, VelocityContext vltContext)
        {
            ResultEntity result = new ResultEntity();            
            string codeText = "";

            try
            {
                Template vltTemplate = vltEngine.GetTemplate("DaoTemplate.tt");
                using (System.IO.StringWriter vltWriter = new System.IO.StringWriter())
                {
                    vltTemplate.Merge(vltContext, vltWriter);
                    codeText = vltWriter.GetStringBuilder().ToString();
                }
            }
            catch(Exception ex)
            {
                DevelopAssistant.Common.NLogger.LogError(ex);
            }
            
            result.Name = entityName;
            result.TypeName = "DAO";
            result.SnippetCode = codeText;
            return result;
        }

        private static ResultEntity GenerateIServiceCode(string entityName, GenerateSettings settings, VelocityEngine vltEngine, VelocityContext vltContext)
        {
            ResultEntity result = new ResultEntity();
            Template vltTemplate = vltEngine.GetTemplate("IServiceTemplate.tt");
            string codeText = "";
            using (System.IO.StringWriter vltWriter = new System.IO.StringWriter())
            {
                vltTemplate.Merge(vltContext, vltWriter);
                codeText = vltWriter.GetStringBuilder().ToString();
            }
            result.Name = entityName;
            result.TypeName = "IService";
            result.SnippetCode = codeText;
            return result;
        }

        private static ResultEntity GenerateServiceImplCode(string entityName, GenerateSettings settings, VelocityEngine vltEngine, VelocityContext vltContext)
        {
            ResultEntity result = new ResultEntity();
            Template vltTemplate = vltEngine.GetTemplate("ServiceImplTemplate.tt");
            string codeText = "";
            using (System.IO.StringWriter vltWriter = new System.IO.StringWriter())
            {
                vltTemplate.Merge(vltContext, vltWriter);
                codeText = vltWriter.GetStringBuilder().ToString();
            }
            result.Name = entityName;
            result.TypeName = "ServiceImpl";
            result.SnippetCode = codeText;
            return result;
        }

        private static ResultEntity GenerateControllerCode(string entityName, GenerateSettings settings, VelocityEngine vltEngine, VelocityContext vltContext)
        {
            ResultEntity result = new ResultEntity();
            Template vltTemplate = vltEngine.GetTemplate("ControllerTemplate.tt");
            string codeText = "";
            using (System.IO.StringWriter vltWriter = new System.IO.StringWriter())
            {
                vltTemplate.Merge(vltContext, vltWriter);
                codeText = vltWriter.GetStringBuilder().ToString();
            }
            result.Name = entityName;
            result.TypeName = "Controller";
            result.SnippetCode = codeText;
            return result;
        }

        public static List<ResultEntity> GenerateCode(string tableName, GenerateSettings settings, DataTable dataTable,List<RequestEntity> list,Action<ReportProgressValueEventArgent> action)
        {
            List<ResultEntity> result = new List<ResultEntity>();

            string templetePath = string.Format("{0}\\{1}\\", currentDirectory.Trim('\\'), templete);

            VelocityEngine vltEngine = new VelocityEngine();
            vltEngine.SetProperty(RuntimeConstants.RESOURCE_LOADER, "file");
            vltEngine.SetProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, templetePath);
            vltEngine.Init();

            DevelopAssistant.Common.StringPlus stringPlus = new Common.StringPlus();

            string EntityName = stringPlus.Underline2Camel(tableName.Replace(settings.NameSpace + "_", "").Replace(settings.Modelpath + "_", ""), false);
            string InstanceName = stringPlus.Underline2Camel(tableName.Replace(settings.NameSpace + "_", "").Replace(settings.Modelpath + "_", ""), true);

            int index = 0;
            QueryWrapper queryWrapper = new QueryWrapper();
            List<DataTableColumnInfo> DataTableColumns = new List<DataTableColumnInfo>();
            foreach (DataRow row in dataTable.Rows)
            {
                DataTableColumnInfo ColumnInfo = new DataTableColumnInfo();
                ColumnInfo.IdType = ToIdType(row["TypeName"] + "");
                ColumnInfo.ColumnName = row["ColumnName"] + "";
                ColumnInfo.PropertyName = stringPlus.Underline2Camel(row["ColumnName"] + "", true);
                ColumnInfo.Description = row["Describ"] + "";
                ColumnInfo.ColumnDataType = (row["TypeName"] + "").ToUpper();
                ColumnInfo.PropertyDataType = ToJavaDataType(row["TypeName"] + "");
                ColumnInfo.JdbcType = ToJdbcType(ColumnInfo.PropertyDataType);
                DoColumnInfoParse(ColumnInfo, row["Cisnull"] + "");
                DataTableColumns.Add(ColumnInfo);

                if (index == 1)
                {
                    queryWrapper.EQ(ColumnInfo.ColumnName, "" + ColumnInfo.ColumnName + "");
                }

                if (ColumnInfo.PropertyDataType.Equals("Date"))
                {
                    queryWrapper.OrderBy(ColumnInfo.ColumnName,"DESC");
                }

                index = index + 1;
            }                    

            double progressIndex = 1.0;
            double progressTotal = list.Count * 1.0d;

            action(new ReportProgressValueEventArgent("开始生成代码", ReportProgressState.started));

            foreach (RequestEntity reqest in list)
            {
                VelocityContext vltContext = new VelocityContext();
                vltContext.Put("PakageName", settings.NameSpace);
                vltContext.Put("TableName", tableName);
                vltContext.Put("EntityName", EntityName);
                vltContext.Put("InstanceName", InstanceName);
                vltContext.Put("EntityProperties", DataTableColumns);
                vltContext.Put("ParamaryKey", settings.ParamaryKey);

                ResultEntity resultEntity = null;

                if (reqest.Name.Equals("Entity"))
                {
                    resultEntity = GenerateEntityCode(EntityName, settings, vltEngine, vltContext);
                }
                else if (reqest.Name.Equals("Mapper"))
                {
                    resultEntity = GenerateMapperCode(EntityName, settings, vltEngine, vltContext, DataTableColumns, queryWrapper);
                }
                else if (reqest.Name.Equals("DAO"))
                {
                    resultEntity = GenerateDaoCode(EntityName, settings, vltEngine, vltContext);
                }
                else if (reqest.Name.Equals("IService"))
                {
                    resultEntity = GenerateIServiceCode(EntityName, settings, vltEngine, vltContext);
                }
                else if (reqest.Name.Equals("ServiceImpl"))
                {
                    resultEntity = GenerateServiceImplCode(EntityName, settings, vltEngine, vltContext);
                }
                else if (reqest.Name.Equals("Controller"))
                {
                    resultEntity = GenerateControllerCode(EntityName, settings, vltEngine, vltContext);
                }

                double progressValue = progressIndex / progressTotal * 100;

                action(new ReportProgressValueEventArgent("已生成 " + reqest.Name + " 代码", resultEntity, progressValue));

                progressIndex++;
            }

            action(new ReportProgressValueEventArgent("生成代码完成", ReportProgressState.finished));

            return result;
        }

    }
}
